package com.legendframework.core.dao.yaml.support;

import com.legendframework.core.dao.Return;
import com.legendframework.core.dao.annotation.Column;
import com.legendframework.core.dao.annotation.TableId;
import com.legendframework.core.dao.enums.IEnum;
import com.legendframework.core.dao.exception.DaoException;
import com.legendframework.core.dao.support.ClassUtils;
import com.legendframework.core.dao.utils.DaoStringUtils;
import com.legendframework.core.dao.utils.EnumUtils;
import com.legendframework.core.support.lang3.StringUtils;
import org.bukkit.configuration.file.YamlConstructor;
import org.bukkit.configuration.file.YamlRepresenter;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.representer.Representer;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public abstract class AbstractBaseYml<T> {

    private final DumperOptions yamlOptions = new DumperOptions();
    private final Representer yamlRepresenter = new YamlRepresenter();
    private final Yaml yaml;
    protected final String path;
    protected final Class<T> entityType;

    protected Map<String, List<String>> annotationStorage = new LinkedHashMap<>();

    // 新增保留注释字段
    private final static String COMMENT_PREFIX = "%注释% #";
    private final static String COMMENT_VALUE_PREFIX = "注释";
    protected final static String INDEX = "INDEX";
    private final static String COMMENT_PREFIX_SYMBOL_STR_KEY = "'%注释% ";
    private final static String COMMENT_SUFFIX_STR_VALUE = "': 注释";
    private final static String FROM_REGEX = "( *)(#.*)";
    private final static Pattern FROM_PATTERN = Pattern.compile(FROM_REGEX);
    private final static String TO_REGEX = "( *)(- )*" + "(" + COMMENT_PREFIX_SYMBOL_STR_KEY + ")" + "(#.*)" + "(" + COMMENT_SUFFIX_STR_VALUE + ")";
    private final static Pattern toPattern = Pattern.compile(TO_REGEX);
    private final static Pattern countSpacePattern = Pattern.compile("( *)(- )*(.*)");
    private final static int commentSplitWidth = 90;
    private final static String newLine = "\n";

    public AbstractBaseYml(String path , Class<T> entityType) {
        this.yamlOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        this.yamlRepresenter.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
        this.yaml = new Yaml(new YamlConstructor(), yamlRepresenter, yamlOptions);
        this.path = path;
        this.entityType = entityType;
    }

    public Class<T> getEntityType() {
        return entityType;
    }

    /**
     * 读取Yaml文件，并将这个文件转换为java对象
     */
    protected Object toGet() {
        StringBuilder builder = null;
        try (FileInputStream stream = new FileInputStream(new File(path));
             InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8);
             BufferedReader input = new BufferedReader(reader)
        ) {
            builder = new StringBuilder();
            String line;
            while ((line = input.readLine()) != null) {
                builder.append(line);
                builder.append('\n');
            }
            Return<Object, Map<String, List<String>>> deserialize = deserialize(this.loadFromString(builder.toString()));
            annotationStorage = deserialize.getR2();
            return fieldColumnDeserialize(deserialize.getR1());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 保存硬盘
     */
    void toSave(T t) {
        Object object = fieldColumnSerialize(t);
        Object serialize = serialize(object);
        String s = saveToString(serialize);
        try (FileOutputStream fileOutputStream = new FileOutputStream(new File(path));
             OutputStreamWriter writer = new OutputStreamWriter(fileOutputStream, StandardCharsets.UTF_8)) {
            writer.write(s);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    protected Map<String, Object> loadFromString(String contents) {
        String[] parts = contents.split(newLine);
        List<String> lastComments = new ArrayList<>();
        StringBuilder builder = new StringBuilder();
        for (String part : parts) {
            Matcher matcher = FROM_PATTERN.matcher(part);
            if (matcher.find()) {
                String originComment = matcher.group(2);
                String[] splitComments = StringUtils.split(originComment, commentSplitWidth);
                for (int i = 0; i < splitComments.length; i++) {
                    String comment = splitComments[i];
                    if (i == 0) {
                        comment = comment.substring(1);
                    }
                    comment = "#" + comment;
                    lastComments.add(comment.replaceAll("\\.", "．").replaceAll("'", "＇").replaceAll(":", "："));
                }
            } else {
                matcher = countSpacePattern.matcher(part);
                if (matcher.find() && !lastComments.isEmpty()) {
                    for (String comment : lastComments) {
                        builder.append(matcher.group(1));
                        String group = matcher.group(2);
                        builder.append(group == null ? "" : group);
                        builder.append(COMMENT_PREFIX_SYMBOL_STR_KEY);
                        builder.append(comment);
                        builder.append(COMMENT_SUFFIX_STR_VALUE);
                        builder.append(newLine);
                    }
                    lastComments.clear();
                }
                builder.append(part);
                builder.append(newLine);
            }
        }
        return this.yaml.load(builder.toString());
    }

    protected String saveToString(Object object) {
        String dump = this.yaml.dump(object);
        if (dump.equals("{}\n")) {
            dump = "";
        }
        StringBuilder savcontent = new StringBuilder();
        String[] parts = dump.split(newLine);
        for (String part : parts) {
            Matcher matcher = toPattern.matcher(part);
            if (matcher.find() && matcher.groupCount() == 5) {
                String group = matcher.group(1);
                part = (group == null ? "" : group) + matcher.group(4);
            }
            savcontent.append(part.replaceAll("．", ".").replaceAll("＇", "'").replaceAll("：", ":"));
            savcontent.append(newLine);
        }
        String s = savcontent.toString();
        s = s.replace(": ''",": ");
        s = s.replace(": \"\"",": ");
        return s;
    }

    protected Object serialize(Object object) {
        return serialize(null, object);
    }

    protected Return<Object, Map<String, List<String>>> deserialize(Object object) {
        return deserialize(null, object);
    }

    /**
     * Yaml序列化，将JavaBean转换为可读性的Yaml，这个Yaml会附带注释
     *
     * @param object 需要存储的对象
     * @return
     */
    protected Object serialize(String node, Object object) {
        if (object == null) {
            return "";
        }
        if (object instanceof Map) {
            Map<String, Object> result = new LinkedHashMap<>();
            Map<String, Object> map = (Map<String, Object>) object;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                List<String> info = annotationStorage.get((StringUtils.isNotEmpty(node) ? node + "." : "") + key);
                if (info != null && !info.isEmpty()) {
                    for (String s : info) {
                        result.put(COMMENT_PREFIX + s, COMMENT_VALUE_PREFIX);
                    }
                }
                result.put(key, serialize(StringUtils.isNotEmpty(node) ? node + "." + key : key, entry.getValue()));
            }
            return result;
        }
        if (object instanceof Collection || object.getClass().isArray()) {
            Collection list;
            if (object instanceof Collection){
                list = (Collection) object;
            }else {
                list = Arrays.asList((Object[]) object);
            }
            List<Object> result = new ArrayList<>();
            int i = 0;
            for (Object item : list) {
                List<String> info = annotationStorage.get((StringUtils.isNotEmpty(node) ? node + "#" : "") + INDEX + (i + 1));
                if (info != null && !info.isEmpty()) {
                    for (String s : info) {
                        Map<String, Object> infoMap = new HashMap<>();
                        infoMap.put(COMMENT_PREFIX + s, COMMENT_VALUE_PREFIX);
                        result.add(infoMap);
                    }
                }
                result.add(serialize((StringUtils.isNotEmpty(node) ? node + "#" : "") + INDEX + (i + 1), item));
                i++;
            }
            return result;
        }
        return object;
    }

    /**
     * Yaml反序列化，将Yaml数据对象转换为可用的Map，并将注释索引返回
     * 建立Yaml注释索引Map
     *
     * @param node   节点
     * @param object YamlMap
     * @return 返回清除掉注释后得数据对象
     */
    protected Return<Object, Map<String, List<String>>> deserialize(String node, Object object) {
        Map<String, List<String>> storage = new HashMap<>();
        if (object == null) {
            return Return.build(null,storage);
        }
        List<String> info = new ArrayList<>();
        if (object instanceof Map) {
            Map<String, Object> result = new LinkedHashMap<>();
            Map<String, Object> map = (Map<String, Object>) object;
            for (String key : map.keySet()) {
                if (key.startsWith(COMMENT_PREFIX) && COMMENT_VALUE_PREFIX.equals(map.get(key))) {
                    info.add(key.replaceFirst(COMMENT_PREFIX, ""));
                    continue;
                }
                if (info.size() > 0) {
                    storage.put((StringUtils.isNotEmpty(node) ? node + "." : "") + key, new ArrayList<>(info));
                    info.clear();
                }
                Return<Object, Map<String, List<String>>> returnObj = deserialize((StringUtils.isNotEmpty(node) ? node + "." : "") + key, map.get(key));
                storage.putAll(returnObj.getR2());
                result.put(key, returnObj.getR1());
            }
            return Return.build(result, storage);
        }
        if (object instanceof Collection || object.getClass().isArray()) {
            int listCount = 0;
            Collection list;
            if (object instanceof Collection) {
                list = (Collection) object;
            }else {
                list = Arrays.asList((Object[]) object);
            }
            List<Object> result = new ArrayList<>();
            int i = 0;
            for (Object item : list) {
                if (item instanceof Map && ((Map<String, Object>) item).size() == 1 && ((Map<String, Object>) item).keySet().iterator().next().startsWith(COMMENT_PREFIX) && COMMENT_VALUE_PREFIX.equals(((Map<String, Object>) item).values().iterator().next())) {
                    Map<String, Object> listMap = (Map<String, Object>) item;
                    String key = listMap.keySet().iterator().next();
                    info.add(key.replaceFirst(COMMENT_PREFIX, ""));
                    listCount++;
                } else {
                    if (info.size() > 0) {
                        storage.put((StringUtils.isNotEmpty(node) ? node + "#" : "") + INDEX + (i + 1 - listCount), new ArrayList<>(info));
                    }
                    Return<Object, Map<String, List<String>>> returnObj = deserialize((StringUtils.isNotEmpty(node) ? node + "#" : "") + INDEX + (i + 1 - listCount), item);
                    storage.putAll(returnObj.getR2());
                    result.add(returnObj.getR1());
                    info.clear();
                }
                i++;
            }
            return Return.build(result, storage);
        }
        if (object.getClass().isArray()) {

        }
        return Return.build(object, new HashMap<>());
    }


    /**
     * 将结果集的Key进行字段映射转换
     * 反序列化
     *
     * @param object 结果集
     * @return
     */
    private Object fieldColumnDeserialize(Object object) {
        if (object instanceof Map) {
            Map<String, Object> result = new HashMap<>();
            for (Map.Entry<String, Object> entry : ((Map<String, Object>) object).entrySet()) {
                String name = entry.getKey();
                Object value = entry.getValue();
                List<Field> fields = ClassUtils.getByClass(entityType);
                for (Field field : fields) {
                    if (field.isAnnotationPresent(TableId.class)) {
                        TableId tableId = field.getAnnotation(TableId.class);
                        if (!"".equals(tableId.value()) && tableId.value().equals(name)) {
                            name = field.getName();
                            break;
                        }
                    }
                    if (field.isAnnotationPresent(Column.class)) {
                        Column column = field.getAnnotation(Column.class);
                        if (column.exist() && !"".equals(column.value()) && column.value().equals(name)) {
                            name = field.getName();
                            break;
                        }
                    }
                }
                result.put(name, fieldColumnDeserialize(value));
            }
            return result;
        }
        if (object instanceof List) {
            List result = new ArrayList();
            for (Object item : (List) object) {
                result.add(fieldColumnDeserialize(item));
            }
            return result;
        }
        return object;
    }

    /**
     * 将结果集的Key进行字段映射转换
     * 序列化
     *
     * @param t 需要序列化的对象
     * @return
     */
    private Object fieldColumnSerialize(T t) {
        if (annotationStorage.isEmpty()) {
            Return<Object, Map<String, List<String>>> mapReturn = fieldColumnSerialize(null, t, true);
            annotationStorage = mapReturn.getR2();
            return mapReturn.getR1();
        } else {
            Return<Object, Map<String, List<String>>> mapReturn = fieldColumnSerialize(null, t, false);
            return mapReturn.getR1();
        }
    }

    /**
     * 序列化的递归逻辑
     * 封装对象中的注释，字段映射等
     * @param node 节点
     * @param object 对象
     * @param isComment 是否输出注释
     * @return
     */
    private Return<Object, Map<String, List<String>>> fieldColumnSerialize(String node, Object object, boolean isComment) {
        Map<String, List<String>> commentMap = new HashMap<>();
        if (object == null) {
            return Return.build(object, commentMap);
        }
        Class cls = object.getClass();
        if (ClassUtils.isScalar(object.getClass())) {
            return Return.build(object, commentMap);
        }
        if (object instanceof Map) {
            Map<String, Object> map = (Map<String, Object>) object;
            Map<String, Object> result = new HashMap<>();
            int i = 0;
            for (String key : map.keySet()) {
                Return<Object, Map<String, List<String>>> objectMapReturn = fieldColumnSerialize((StringUtils.isNotEmpty(node) ? node + "." : "") + key, map.get(key), i == 0);
                commentMap.putAll(objectMapReturn.getR2());
                result.put(key, objectMapReturn.getR1());
                i++;
            }
            return Return.build(result, commentMap);
        }
        if (object instanceof Collection) {
            List result = new ArrayList();
            int i = 0;
            for (Object item : (List) object) {
                Return<Object, Map<String, List<String>>> objectMapReturn = fieldColumnSerialize((StringUtils.isNotEmpty(node) ? node + "#" : "") + "INDEX" + (i + 1), item, isComment);
                commentMap.putAll(objectMapReturn.getR2());
                result.add(objectMapReturn.getR1());
                i++;
            }
            return Return.build(result, commentMap);
        }
        if (object.getClass().isArray()) {
            List result = new ArrayList();
            int i = 0;
            for (Object item : ((Object[]) object)) {
                Return<Object, Map<String, List<String>>> objectMapReturn = fieldColumnSerialize((StringUtils.isNotEmpty(node) ? node + "#" : "") + "INDEX" + (i + 1), item, isComment);
                commentMap.putAll(objectMapReturn.getR2());
                result.add(objectMapReturn.getR1());
                i++;
            }
            return Return.build(result, commentMap);
        }
        if (object.getClass().isEnum()) {
            return Return.build(((Enum) object).name(), commentMap);
        }
        //Entity
        Map<String, Object> result = new LinkedHashMap<>();
        List<Field> fields = ClassUtils.getByClass(object.getClass());
        //字段排序
        List<Field> fieldList = fields.stream().sorted((a, b) -> {
            Column aColumnComment = a.getAnnotation(Column.class);
            Column bColumnComment = b.getAnnotation(Column.class);
            //获取权重值
            int aSort = aColumnComment == null ? -1 : aColumnComment.sort();
            int bSort = bColumnComment == null ? -1 : bColumnComment.sort();
            if ((aSort < 1 && bSort < 1) || aSort == bSort) {
                return 0;
            }
            if (aSort < 1) {
                return 1;
            } else if (bSort < 1) {
                return -1;
            }
            return aSort > bSort ? 1 : -1;
        }).collect(Collectors.toList());

        for (Field field : fieldList) {
            String name = field.getName();
            if (field.isAnnotationPresent(TableId.class)) {
                TableId tableId = field.getAnnotation(TableId.class);
                if (StringUtils.isNotEmpty(tableId.value())) {
                    name = tableId.value();
                }
            }
            if (field.isAnnotationPresent(Column.class)) {
                Column column = field.getAnnotation(Column.class);
                if (!column.exist()) {
                    continue;
                }
                if (StringUtils.isNotEmpty(column.value())) {
                    name = column.value();
                }
                if (column.comments().length > 0 && isComment) {
                    List<String> infos = new ArrayList<>(Arrays.asList(column.comments()));
                    //获取可选范围
                    if (column.isOutPutOptionalRange() && (boolean.class.equals(field.getType()) || Boolean.class.equals(field.getType()) || field.getType().isEnum())) {
                        if (boolean.class.equals(field.getType()) || Boolean.class.equals(field.getType())) {
                            infos.add("可选值: true : 是 | false : 否");
                        } else if (field.getType().isEnum() && IEnum.class.isAssignableFrom(field.getType())) {
                            try {
                                IEnum[] values = EnumUtils.values((Class<IEnum>) field.getType());
                                StringBuilder s = new StringBuilder("可选值: ");
                                for (int i = 0; i < values.length; i++) {
                                    IEnum value = values[i];
                                    if (i != 0) {
                                        s.append(" | ");
                                    }
                                    s.append(value.getCode()).append(" : ").append(value.getName());
                                }
                                infos.add(s.toString());
                            } catch (NoSuchMethodException e) {
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    commentMap.put((StringUtils.isNotEmpty(node) ? node + "." : "") + name, infos);
                }
            }

            //获取值
            Object value = null;
            if (Modifier.isPublic(field.getModifiers())) {
                try {
                    value = field.get(object);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    String getMethodName = DaoStringUtils.fieldNameToGetterMethodName(field.getName());
                    Method method = cls.getMethod(getMethodName);
                    value = method.invoke(object);
                } catch (Throwable e) {
                    throw new DaoException(String.format("字段: [%s] ,没有提供get方法", field.getName()));
                }
            }
            Return<Object, Map<String, List<String>>> objectMapReturn = fieldColumnSerialize((StringUtils.isNotEmpty(node) ? node + "." : "") + name, value, isComment);
            commentMap.putAll(objectMapReturn.getR2());
            result.put(name, objectMapReturn.getR1());
        }
        return Return.build(result, commentMap);
    }

}
